package com.cmfi.event;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import com.cmfi.domain.SubscribeCallBack;
import com.cmfi.domain.PlcEntity.Plc;
import com.cmfi.domain.PlcEntity.PlcNode;
import com.cmfi.service.inter.PlcInter.PlcNodeService;
import com.cmfi.service.inter.PlcInter.PlcService;
import com.cmfi.utils.OpcUaUtil;
 
@Component
public class SpringBootApplicationListener implements ApplicationListener<ApplicationEvent> {

	private final Logger log = LoggerFactory.getLogger(SpringBootApplicationListener.class);
	
	@Autowired
	private PlcNodeService plcNodeService;
	
	@Autowired
	private PlcService plcService;
	
	@Autowired
	private SubscribeCallBack subscribeCallback;

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		log.info("[SpringBootApplicationListener][onApplicationEvent] event:{}", event);
		if (event instanceof ApplicationStartedEvent) {// Springboot启动
			initPlcConnection();// 初始化plc连接池
			initSubscribePlcNode();// 订阅PLC待订阅点位
		}

		if (event instanceof ContextClosedEvent) {// Springboot挂掉
			log.info(
					"[SpringBootApplicationListener][onApplicationEvent] disconnect opcUaClient and shutdown ThreadPoolTaskExecutor");
			// 关闭线程池、关闭plc连接、去订阅
			for (OpcUaClient opcUaClient : plcService.getOpcClientMap().values()) {
				OpcUaUtil.disconnect(opcUaClient);
			}
			OpcUaUtil.shutdownThreadPool();
		}
	}

	/**
	 * 初始化订阅所有该订阅的点位
	 */
	private void initSubscribePlcNode() {
		List<PlcNode> plcNodeList = plcNodeService.getAllPlcNodeNeedSubscribe();
		Map<OpcUaClient, Set<PlcNode>> plcNodeMap = new HashMap<>();
		for (PlcNode plcNode : plcNodeList) {
			if (plcNode == null) {
				continue;
			}
			OpcUaClient opcUaClient = plcService.getOpcClientByConnectAddress(plcNode.getPlcConnectAddress());
			if (plcNodeMap.containsKey(opcUaClient)) {
				Set<PlcNode> nodeSet = plcNodeMap.get(opcUaClient);
				nodeSet.add(plcNode);
			} else {
				Set<PlcNode> nodeSet = new HashSet<>();
				nodeSet.add(plcNode);
				plcNodeMap.put(opcUaClient, nodeSet);
			}
		}
		log.info("[SpringBootApplicationListener][initSubscribePlcNode] nodeMap:{}", plcNodeMap);
		for (Map.Entry<OpcUaClient, Set<PlcNode>> entry : plcNodeMap.entrySet()) {
			OpcUaClient client = entry.getKey();
			Set<PlcNode> namespaceMap = entry.getValue();
			try {
				// 订阅plcNode
				OpcUaUtil.batchSubscribeWithDisconnectReSub(client, namespaceMap, subscribeCallback);
			} catch (Exception e) {
				log.info("[SpringBootApplicationListener][initSubscribePlcNode] e,", e);
			}
		}
	}

	/**
	 * 初始化Plc连接
	 */
	private void initPlcConnection() {
		try {
			Map<String, OpcUaClient>  clientMap = plcService.getOpcClientMap();
			List<Plc> plcs = plcService.getAllPlc();
			for (Plc plc : plcs) {
				String connectAdress = plc.getConnectAddress();
				OpcUaClient client = OpcUaUtil.createClient(connectAdress);
				client.connect().get();
				clientMap.put(connectAdress, client);
			}
		} catch (Exception ex) {
			log.info("[SpringBootApplicationListener][initPlcConnection] ex:{}", ex);
		}
	}

}
